using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;

namespace Toenda.Foundation.Utility {
	/// <summary>
	/// Implements the collection of phonenumbers
	/// </summary>
	public class PhoneNumberList : IEnumerable<PhoneNumber>, ICollection<PhoneNumber>, IList<PhoneNumber> {
		private List<PhoneNumber> _internalList;
		private bool _isReadOnly;

		/// <summary>
		/// Creates a new instance of <see cref="PhoneNumberList"/>
		/// </summary>
		public PhoneNumberList() {
			this._internalList = new List<PhoneNumber>();
			this._isReadOnly = false;
		}

		/// <summary>
		/// Creates a new instance of <see cref="PhoneNumberList"/>
		/// </summary>
		/// <param name="collection"></param>
		public PhoneNumberList(IEnumerable<PhoneNumber> collection) {
			this._internalList = new List<PhoneNumber>();
			this._internalList.AddRange(collection);
			this._isReadOnly = false;
		}

		/// <summary>
		/// Creates a new instance of <see cref="PhoneNumberList"/>
		/// </summary>
		/// <param name="collection"></param>
		public PhoneNumberList(PhoneNumberList collection) {
			this._internalList = new List<PhoneNumber>();
			this._internalList.AddRange(collection);
			this._isReadOnly = false;
		}

		/// <summary>
		/// Get the enumerator
		/// </summary>
		/// <returns></returns>
		public IEnumerator<PhoneNumber> GetEnumerator() {
			return this._internalList.GetEnumerator();
		}

		/// <summary>
		/// Get the enumerator
		/// </summary>
		/// <returns></returns>
		IEnumerator IEnumerable.GetEnumerator() {
			return GetEnumerator();
		}

		/// <summary>
		/// Gets a items from the list from a specific index
		/// </summary>
		/// <param name="index"></param>
		/// <returns></returns>
		public PhoneNumber this[int index] {
			get { return this._internalList[index]; }
			set { this._internalList[index] = value; }
		}

		/// <summary>
		/// Get the amount of all items
		/// </summary>
		public int Count {
			get { return this._internalList.Count; }
		}

		/// <summary>
		/// Gets or sets the total number of elements the internal data structure can hold withour resizing
		/// </summary>
		public int Capacity {
			get { return this._internalList.Capacity; }
			set { this._internalList.Capacity = value; }
		}

		/// <summary>
		/// Gets or sets a value that indicates if the list is readonly
		/// </summary>
		public bool IsReadOnly {
			get { return this._isReadOnly; }
			set { this._isReadOnly = value; }
		}

		/// <summary>
		/// Clear all items from the list
		/// </summary>
		public void Clear() {
			this._internalList.Clear();
		}

		/// <summary>
		/// Adds a item to the list
		/// </summary>
		/// <param name="item"></param>
		public void Add(PhoneNumber item) {
			if(this._isReadOnly) {
				this._internalList.Add(item);
			}
			else {
				throw new Exception("The list is read-only!");
			}
		}

		/// <summary>
		/// Adds a range of items to the collection
		/// </summary>
		/// <param name="collection"></param>
		public void AddRange(IEnumerable<PhoneNumber> collection) {
			if(this._isReadOnly) {
				this._internalList.AddRange(collection);
			}
			else {
				throw new Exception("The list is read-only!");
			}
		}

		/// <summary>
		/// Adds a range of items to the collection
		/// </summary>
		/// <param name="collection"></param>
		public void AddRange(PhoneNumberList collection) {
			if(this._isReadOnly) {
				this._internalList.AddRange(collection);
			}
			else {
				throw new Exception("The list is read-only!");
			}
		}

		/// <summary>
		/// Gets the collection as a read-only collection
		/// </summary>
		/// <returns></returns>
		public ReadOnlyCollection<PhoneNumber> AsReadOnly() {
			return this._internalList.AsReadOnly();
		}
		
		/// <summary>
		/// Searches the entire sorted System.Collection.Generic.List for an 
		/// element using the default comparer and returns the zero-based index of the element
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public int BinarySearch(PhoneNumber item) {
			return this._internalList.BinarySearch(item);
		}

		/// <summary>
		/// Get a range of items beginning from the index
		/// </summary>
		/// <param name="index">The index.</param>
		/// <exception cref="IndexOutOfRangeException"></exception>
		/// <returns></returns>
		public PhoneNumberList GetRange(int index) {
			if(index > this._internalList.Count) {
				throw new IndexOutOfRangeException();
			}

			return new PhoneNumberList(
				this._internalList.GetRange(index, this._internalList.Count - index)
			);
		}

		/// <summary>
		/// Get a range of items beginning from the index and maximum item count is count
		/// </summary>
		/// <param name="index"></param>
		/// <param name="count"></param>
		/// <exception cref="IndexOutOfRangeException"></exception>
		/// <returns></returns>
		public PhoneNumberList GetRange(int index, int count) {
			if(index > this._internalList.Count
			|| ( index + count ) > this._internalList.Count) {
				throw new IndexOutOfRangeException();
			}

			return new PhoneNumberList(
				this._internalList.GetRange(index, count)
			);
		}

		/// <summary>
		/// Insert a item to the specific index
		/// </summary>
		/// <param name="index"></param>
		/// <param name="item"></param>
		/// <exception cref="IndexOutOfRangeException"></exception>
		/// <exception cref="Exception"></exception>
		public void Insert(int index, PhoneNumber item) {
			if(index > this._internalList.Count) {
				throw new IndexOutOfRangeException();
			}

			if(this._isReadOnly) {
				this._internalList.Insert(index, item);
			}
			else {
				throw new Exception("The list is read-only!");
			}
		}

		/// <summary>
		/// Insert a range of items to the specific index
		/// </summary>
		/// <param name="index"></param>
		/// <param name="collection"></param>
		/// <exception cref="IndexOutOfRangeException"></exception>
		/// <exception cref="Exception"></exception>
		public void InsertRange(int index, IEnumerable<PhoneNumber> collection) {
			if(index > this._internalList.Count) {
				throw new IndexOutOfRangeException();
			}

			if(this._isReadOnly) {
				this._internalList.InsertRange(index, collection);
			}
			else {
				throw new Exception("The list is read-only!");
			}
		}

		/// <summary>
		/// Insert a range of items to the specific index
		/// </summary>
		/// <param name="index"></param>
		/// <param name="collection"></param>
		/// <exception cref="IndexOutOfRangeException"></exception>
		/// <exception cref="Exception"></exception>
		public void InsertRange(int index, PhoneNumberList collection) {
			if(index > this._internalList.Count) {
				throw new IndexOutOfRangeException();
			}

			if(this._isReadOnly) {
				this._internalList.InsertRange(index, collection);
			}
			else {
				throw new Exception("The list is read-only!");
			}
		}

		/// <summary>
		/// Copy the list to an array
		/// </summary>
		/// <param name="array"></param>
		/// <param name="arrayIndex"></param>
		public void CopyTo(PhoneNumber[] array, int arrayIndex) {
			this._internalList.CopyTo(array, arrayIndex);
		}

		/// <summary>
		/// Removes an item from the collection
		/// </summary>
		/// <param name="item"></param>
		/// <exception cref="ArgumentNullException"></exception>
		/// <exception cref="Exception"></exception>
		/// <returns></returns>
		public bool Remove(PhoneNumber item) {
			if(item == null) {
				throw new ArgumentNullException("The parameter item cannot be null!");
			}

			if(this._isReadOnly) {
				return this._internalList.Remove(item);
			}
			else {
				throw new Exception("The list is read-only!");
			}
		}

		/// <summary>
		/// Removes all matching items from the collection
		/// </summary>
		/// <param name="match"></param>
		/// <exception cref="Exception"></exception>
		/// <returns></returns>
		public int RemoveAll(Predicate<PhoneNumber> match) {
			if(this._isReadOnly) {
				return this._internalList.RemoveAll(match);
			}
			else {
				throw new Exception("The list is read-only!");
			}
		}

		/// <summary>
		/// Removes an item an a specific index from the collection
		/// </summary>
		/// <param name="index"></param>
		/// <exception cref="IndexOutOfRangeException"></exception>
		/// <exception cref="Exception"></exception>
		public void RemoveAt(int index) {
			if(index > this._internalList.Count) {
				throw new IndexOutOfRangeException();
			}

			if(this._isReadOnly) {
				this._internalList.RemoveAt(index);
			}
			else {
				throw new Exception("The list is read-only!");
			}
		}

		/// <summary>
		/// Removes a range of items from the collection beginning at the index and counting to the end
		/// </summary>
		/// <param name="index"></param>
		/// <exception cref="IndexOutOfRangeException"></exception>
		/// <exception cref="Exception"></exception>
		public void RemoveRange(int index) {
			if(index > this._internalList.Count) {
				throw new IndexOutOfRangeException();
			}

			if(this._isReadOnly) {
				this._internalList.RemoveRange(index, this._internalList.Count - index);
			}
			else {
				throw new Exception("The list is read-only!");
			}
		}

		/// <summary>
		/// Removes a range of items from the collection beginning at the index and counting to the parameter count
		/// </summary>
		/// <param name="index"></param>
		/// <param name="count"></param>
		/// <exception cref="IndexOutOfRangeException"></exception>
		/// <exception cref="Exception"></exception>
		public void RemoveRange(int index, int count) {
			if(index > this._internalList.Count
			|| (index + count) > this._internalList.Count) {
				throw new IndexOutOfRangeException();
			}

			if(this._isReadOnly) {
				this._internalList.RemoveRange(index, count);
			}
			else {
				throw new Exception("The list is read-only!");
			}
		}

		/// <summary>
		/// Removes a range of items from the collection
		/// </summary>
		/// <param name="collection"></param>
		/// <exception cref="ArgumentNullException"></exception>
		/// <exception cref="Exception"></exception>
		public void RemoveRange(IEnumerable<PhoneNumber> collection) {
			if(collection == null) {
				throw new ArgumentNullException("The parameter collection cannot be null!");
			}

			if(this._isReadOnly) {
				foreach(PhoneNumber cc in collection) {
					this._internalList.Remove(cc);
				}
			}
			else {
				throw new Exception("The list is read-only!");
			}
		}

		/// <summary>
		/// Removes a range of items from the collection
		/// </summary>
		/// <param name="collection"></param>
		/// <exception cref="ArgumentNullException"></exception>
		/// <exception cref="Exception"></exception>
		public void RemoveRange(PhoneNumberList collection) {
			if(collection == null) {
				throw new ArgumentNullException("The parameter collection cannot be null!");
			}

			if(this._isReadOnly) {
				foreach(PhoneNumber cc in collection) {
					this._internalList.Remove(cc);
				}
			}
			else {
				throw new Exception("The list is read-only!");
			}
		}

		/// <summary>
		/// Get sthe index of a item
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public int IndexOf(PhoneNumber item) {
			return this._internalList.IndexOf(item);
		}

		/// <summary>
		/// Checks if the collection containing the item
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public bool Contains(PhoneNumber item) {
			return this._internalList.Contains(item);
		}

		/// <summary>
		/// Checks if the item exist in the collection
		/// </summary>
		/// <param name="match"></param>
		/// <returns></returns>
		public bool Exists(Predicate<PhoneNumber> match) {
			return this._internalList.Exists(match);
		}

		/// <summary>
		/// Finds an item that match
		/// </summary>
		/// <param name="match"></param>
		/// <returns></returns>
		public PhoneNumber Find(Predicate<PhoneNumber> match) {
			return this._internalList.Find(match);
		}

		/// <summary>
		/// Find items that match
		/// </summary>
		/// <param name="match"></param>
		/// <returns></returns>
		public PhoneNumberList FindAll(Predicate<PhoneNumber> match) {
			return new PhoneNumberList(this._internalList.FindAll(match));
		}

		/// <summary>
		/// Convert the collection to a array
		/// </summary>
		/// <returns></returns>
		public PhoneNumber[] ToArray() {
			return this._internalList.ToArray();
		}
	}
}
